// $URL: http://mingw-lib.googlecode.com/svn/trunk/working/avcodec_to_widget_8/nam.h $
// $Rev: 287 $
// $Author: akio.miyoshi $
// $Date:: 2010-07-27 06:46:59 +0000#$

#ifndef NETWORKACCESSMANAGER_H
#define NETWORKACCESSMANAGER_H

#include "stable.h"

#include "private/qnetworkaccessbackend_p.h"

class NetworkAccessBackendFactory : public QObject
{
    Q_OBJECT
public:
    NetworkAccessBackendFactory(QObject * parent = 0) : QObject(parent)
    {
    }
    virtual QObject *create(QNetworkAccessManager * manager,
                            QNetworkAccessManager::Operation op,
                            const QNetworkRequest & request,
                            QIODevice * outgoingData) const
    { return 0; }
};

class NetworkAccessManager : public QNetworkAccessManager
{
    Q_OBJECT
public:
    NetworkAccessManager(QObject *parent = 0);
    virtual QNetworkReply *createRequest(
            Operation op,
            const QNetworkRequest & request,
            QIODevice * outgoingData = 0);
    virtual QNetworkReply *createRequestWithBackend(
            Operation op,
            const QNetworkRequest &request,
            QIODevice *outgoingData = 0,
            QObject *backend = 0);
    void setBackendFactory(NetworkAccessBackendFactory *a_factory);
protected:
    NetworkAccessBackendFactory *m_factory;
};

class HttpAccessBackend: public QNetworkAccessBackend
{
    Q_OBJECT
public:
    explicit HttpAccessBackend(QNetworkAccessManager * nam);
    virtual ~HttpAccessBackend();

    //
    virtual class NetworkHttpReply *createHttpReply() = 0;
    //
    QNetworkAccessManager *networkAccessManager();
    //
    QIODevice *uploadDevice();
    void setUploadDevice(QIODevice *a_device);
    //
    bool isHeadOperation()   { return (operation() == QNetworkAccessManager::HeadOperation); }
    bool isGetOperation()    { return (operation() == QNetworkAccessManager::GetOperation); }
    bool isPutOperation()    { return (operation() == QNetworkAccessManager::PutOperation); }
    bool isPostOperation()   { return (operation() == QNetworkAccessManager::PostOperation); }
    bool isDeleteOperation() { return (operation() == QNetworkAccessManager::DeleteOperation); }
    //
    virtual void open();
    virtual void downstreamReadyWrite();
    // this indicates that the user closed the stream while the reply isn't finished yet
    virtual void closeDownstreamChannel() {}
    // HTTP backend does not support waitForReadyRead()
    virtual bool waitForDownstreamReadyRead(int msecs) { return false; }
    // we return true since HTTP needs to send PUT/POST data again after having authenticated
    virtual bool needsResetableUploadData() { return true; }
    virtual void copyFinished(QIODevice *dev);
    // SSL
    virtual void ignoreSslErrors();
    virtual void ignoreSslErrors(const QList<QSslError> & errors);
    virtual void fetchSslConfiguration(QSslConfiguration & config) const;
    virtual void setSslConfiguration(const QSslConfiguration & config);

private slots:
    void finished(); // override
    void namAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
    void replyAuthenticationRequired(QAuthenticator *authenticator);
    void replyReadyRead();
    void replyFinished();
    void replyMetaDataChanged();
    void replyError(QNetworkReply::NetworkError errorCode);
    void replyUploadProgress(qint64 bytesSent, qint64 bytesTotal);
    void replyDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);

private:
    QNetworkAccessManager *m_nam;
    class NetworkHttpReply *m_httpReply;
    QIODevice * m_uploadDevice;
#ifndef QT_NO_OPENSSL
    QSslConfiguration *m_pendingSslConfiguration;
    bool m_pendingIgnoreAllSslErrors;
    QList<QSslError> m_pendingIgnoreSslErrorsList;
#endif

    void p_disconnectFromHttp();
    void p_readFromHttp();
    void p_checkForRedirect(const int statusCode);
};

class NetworkHttpReply : public QNetworkAccessManager
{
    Q_OBJECT
public:
    explicit NetworkHttpReply(HttpAccessBackend *parent)
                : QNetworkAccessManager(parent)
                , m_backend(parent)
    {
        QNetworkAccessManager::setCookieJar(0);
        this->setParent(parent);
    }
    ~NetworkHttpReply() {}

    // backend()
    HttpAccessBackend *backend()
    {
        return m_backend;
    }

    // raw headers:
    virtual bool hasRawHeader(const QByteArray &headerName) const = 0;
    virtual QList<QByteArray> rawHeaderList() const = 0;
    virtual QByteArray rawHeader(const QByteArray &headerName) const = 0;

    // attributes
    virtual QVariant attribute(QNetworkRequest::Attribute code) const = 0;

    // reading
    virtual qint64 bytesAvailable() = 0;
    virtual QByteArray read(qint64 a_max) = 0;

    // errorString()
    virtual QString errorString() const
    {
        return "";
    }

    // SSL
    virtual void ignoreSslErrors()
    {
    }
    virtual void ignoreSslErrors(const QList<QSslError> & errors)
    {
    }
    virtual QSslConfiguration sslConfiguration() const
    {
        return QSslConfiguration::defaultConfiguration();
    }
    virtual void setSslConfiguration(const QSslConfiguration & config)
    {
    }

Q_SIGNALS:
    void authenticationRequired(QAuthenticator *authenticator); // from QNetworkAccessManager
    void readyRead();
    void metaDataChanged();
    void finished();
    void error(QNetworkReply::NetworkError code);
    void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
    void sslErrors(const QList<QSslError> & errors);

protected Q_SLOTS:
    // emit handlers
    virtual void emit_namAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
    {
        Q_UNUSED(reply);
        emit authenticationRequired(authenticator);
    }
    //[metaDataChanged()]
    virtual void emit_metaDataChanged() { emit metaDataChanged(); }
    void enqueue_metaDataChanged()
    {
        QMetaObject::invokeMethod(this, "emit_metaDataChanged", Qt::QueuedConnection);
    }
    //[readyRead()]
    virtual void emit_readyRead() { emit readyRead(); }
    void enqueue_readyRead()
    {
        QMetaObject::invokeMethod(this, "emit_readyRead", Qt::QueuedConnection);
    }
    //[finished()]
    virtual void emit_finished() { emit finished(); }
    void enqueue_finished()
    {
        QMetaObject::invokeMethod(this, "emit_finished", Qt::QueuedConnection);
    }
    //[error()]
    virtual void emit_error(QNetworkReply::NetworkError code) { emit error(code); }
    //[uploadProgress()]
    virtual void emit_uploadProgress(qint64 bytesSent, qint64 bytesTotal)
    {
        emit uploadProgress(bytesSent, bytesTotal);
    }
#ifndef QT_NO_OPENSSL
    //[sslErrors()]
    virtual void emit_sslErrors(const QList<QSslError> & errors)
    {
        emit sslErrors(errors);
    }
#endif
protected:
    virtual void p_connectQNetworkReply(QNetworkReply * a_netReply)
    {
        //NAM::authenticationRequired()
        connect(a_netReply->manager(),
                SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
                SLOT(emit_namAuthenticationRequired(QNetworkReply*,QAuthenticator*)));
        //readyRead()
        connect(a_netReply, SIGNAL(readyRead()), SLOT(emit_readyRead()));
        //metaDataChanged()
        connect(a_netReply, SIGNAL(metaDataChanged()), SLOT(emit_metaDataChanged()));
        //finished()
        connect(a_netReply, SIGNAL(finished()), SLOT(emit_finished()));
        //error()
        connect(a_netReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(emit_error(QNetworkReply::NetworkError)));
        //uploadProgress()
        connect(a_netReply, SIGNAL(uploadProgress(qint64,qint64)), SLOT(emit_uploadProgress(qint64,qint64)));
#ifndef QT_NO_OPENSSL
        connect(a_netReply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(emit_sslErrors(QList<QSslError>)));
#endif
    }
protected:
    HttpAccessBackend *m_backend;
    ////QNetworkReply *m_netReply;
};

#endif // NETWORKACCESSMANAGER_H
